/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.action.core.contentPage;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.common.util.DateUtil;
import com.blandware.atleap.model.core.ContentField;
import com.blandware.atleap.model.core.ContentFieldValue;
import com.blandware.atleap.model.core.ContentPage;
import com.blandware.atleap.model.core.Layout;
import com.blandware.atleap.search.SearchManager;
import com.blandware.atleap.service.core.ContentFieldManager;
import com.blandware.atleap.service.core.LayoutManager;
import com.blandware.atleap.service.core.PageManager;
import com.blandware.atleap.service.exception.BeanAlreadyExistsException;
import com.blandware.atleap.service.exception.OwnerNotFoundException;
import com.blandware.atleap.webapp.action.core.BaseAction;
import com.blandware.atleap.webapp.form.core.ContentPageForm;
import com.blandware.atleap.webapp.struts.HeritableComponentDefinition;
import com.blandware.atleap.webapp.util.core.CacheUtil;
import com.blandware.atleap.webapp.util.core.WebappConstants;
import com.blandware.atleap.webapp.util.core.WebappUtil;
import org.apache.commons.validator.GenericValidator;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.config.ModuleConfig;
import org.apache.struts.tiles.TilesUtil;
import org.apache.struts.util.ModuleUtils;
import org.apache.struts.util.RequestUtils;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * <p>Creates new content page
 * </p>
 * <p><a href="CreateContentPageAction.java.html"><i>View Source</i></a></p>
 * <p/>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @author Andrey Grebnev <a href="mailto:andrey.grebnev@blandware.com">&lt;andrey.grebnev@blandware.com&gt;</a>
 * @version $Revision: 1.44 $ $Date: 2008/04/29 17:48:06 $
 * @struts.action path="/core/contentPage/create"
 * name="contentPageForm"
 * scope="request"
 * input="inputForward"
 * validate="true"
 * roles="core-contentPage-create"
 * @struts.action-forward name="inputForward"
 * path=".core.contentPage.create"
 * @struts.action-forward name="callAssignRoles"
 * path="/core/contentPage/callAssignRoles.do"
 * redirect="false"
 * @struts.action-forward name="listContentPages"
 * path="/core/contentPage/list.do"
 * redirect="true"
 * @struts.action-forward name="viewContentPage"
 * path="/core/contentPage/view.do"
 * redirect="true"
 * @struts.action-forward name="unsatisfiable"
 * path="/core/contentPage/list.do"
 */
public final class CreateContentPageAction extends BaseAction {
	/**
	 * @param mapping  The ActionMapping used to select this instance
	 * @param form     The optional ActionForm bean for this request (if any)
	 * @param request  The HTTP request we are proceeding
	 * @param response The HTTP response we are creating
	 * @return an ActionForward instance describing where and how
	 *         control should be forwarded, or null if response
	 *         has already been completed
	 */
	public ActionForward execute(ActionMapping mapping, ActionForm form,
	                             HttpServletRequest request, HttpServletResponse response) throws Exception {

        HttpSession session = request.getSession();
        ServletContext servletContext = session.getServletContext();
        String requestUrl = (String) session.getAttribute(WebappConstants.REDIRECT_URL_KEY);

        if ( !isCancelled(request) ) {
			ContentPageForm contentPageForm = (ContentPageForm) form;

			if ( !WebappUtil.hasCorrectValues(contentPageForm.getTitleMap()) ) {
				// title must be specified
				ActionMessages errors = new ActionMessages();
				errors.add("title", new ActionMessage("core.commons.errors.required", getMessage(request, "core.page.form.title")));
				saveErrors(request, errors, false);
				saveToken(request);
				return mapping.getInputForward();
			}
            
            // check publication and expiration dates
            Date publicationDate;
            Date expirationDate;
            boolean publicationDateIsValid = false;
            try {
                publicationDate = DateUtil.parseDate(contentPageForm.getPublicationDate(), RequestUtils.getUserLocale(request, null));
                publicationDateIsValid = true;
                expirationDate = DateUtil.parseDate(contentPageForm.getExpirationDate(), RequestUtils.getUserLocale(request, null));
            } catch (ParseException e) {
                ActionMessages errors = new ActionMessages();
                errors.add("contentPageInvalidDate", new ActionMessage("core.commons.errors.customDate",
                            publicationDateIsValid ? contentPageForm.getExpirationDate() : contentPageForm.getPublicationDate()));
                saveErrors(request, errors);
                saveToken(request);
                return mapping.getInputForward();
            }

            if ( publicationDate.compareTo(expirationDate) >= 0 ) {
                ActionMessages errors = new ActionMessages();
                errors.add("contentPageInvalidDate", new ActionMessage("core.contentPage.errors.invalidPublicationDate"));
                saveErrors(request, errors);
                saveToken(request);
                return mapping.getInputForward();
            }

            Date now = new Date();
            if (expirationDate.compareTo(now) <= 0) {
                ActionMessages errors = new ActionMessages();
                errors.add("contentPageInvalidDate", new ActionMessage("core.contentPage.errors.invalidExpirationDate"));
                saveErrors(request, errors);
                saveToken(request);
                return mapping.getInputForward();
            }

            // check URI. If there is an action with the same path, return error
			String uri = WebappConstants.CONTENT_PAGES_URI_PREFIX + contentPageForm.getUri();
			ModuleConfig moduleConfig = ModuleUtils.getInstance().getModuleConfig(request, getServlet().getServletContext());
			String prefix = moduleConfig.getPrefix();
			if ( log.isDebugEnabled() ) {
				log.debug("Prefix: " + prefix);
			}

			String actionPath = prefix + uri;

			if ( log.isDebugEnabled() ) {
				log.debug("Searching for action with path=" + actionPath);
			}

			if ( moduleConfig.findActionConfig(actionPath) != null ) {
				// action with same uri exists
				if ( log.isWarnEnabled() ) {
					log.warn("Action was found - returning input");
				}
				ActionMessages errors = new ActionMessages();
				errors.add("contentPageIncorrectUri", new ActionMessage("core.contentPage.errors.incorrectUri"));
				saveErrors(request, errors);
				saveToken(request);
				return mapping.getInputForward();
			}

            ContentPage contentPage = new ContentPage();
			WebappUtil.copyProperties(contentPage, contentPageForm, request);

            WebappUtil.fillFieldValueWithDefaultLocaleValue(contentPageForm.getTitleMap(), contentPage.getTitle(), servlet.getServletContext());
            contentPage.setTitle(contentPageForm.getTitleMap());
            WebappUtil.copyFieldValue(contentPage.getDescription(), contentPageForm.getDescriptionMap());
            WebappUtil.copyFieldValue(contentPage.getKeywords(), contentPageForm.getKeywordsMap());

            contentPage.setUri(uri);
			PageManager pageManager = (PageManager) getBean(Constants.PAGE_MANAGER_BEAN);
			Long layoutId = Long.valueOf(contentPageForm.getLayoutId());
			Long contentPageId;
            try {
				contentPageId = pageManager.createContentPage(contentPage, layoutId);

				//index content page
				LayoutManager layoutManager = (LayoutManager) getBean(Constants.LAYOUT_MANAGER_BEAN);
				Layout layout = layoutManager.retrieveLayout(layoutId);

				if ( contentPage.getActive().booleanValue() ) {
                    SearchManager searchManager = SearchManager.getInstance(servletContext);
					searchManager.reIndexPage(contentPage, request);
				}

				String overrideFields = contentPageForm.getOverrideFields();
				if ( !"none".equalsIgnoreCase(overrideFields) ) {
					boolean all = "all".equalsIgnoreCase(overrideFields);
					boolean empty = "empty".equalsIgnoreCase(overrideFields);
                    boolean emptyIndexless = "emptyIndexless".equalsIgnoreCase(overrideFields);
                    if ( all || empty || emptyIndexless ) {

						// get sequence of layouts
						LinkedList layouts = new LinkedList();
						layouts.addFirst(layout);
						String definition = layout.getDefinition();
						while ( definition != null ) {
							definition = ((HeritableComponentDefinition) TilesUtil.getDefinition(definition, request, servlet.getServletContext())).getExtends();
							if ( definition != null ) {
								layout = layoutManager.findLayoutByDefinition(definition);
								if ( layout != null ) {
									layouts.addFirst(layout);
								}
							}
						}

						// get content fields
						Map contentFields = new HashMap();
						for ( Iterator i = layouts.iterator(); i.hasNext(); ) {
							layout = (Layout) i.next();
							contentFields.putAll(layout.getContentFieldsMap());
						}

						// retrieve created content page in order to get its internal fields
						contentPage = pageManager.retrieveContentPage(contentPageId);

						// remove internal fields of content page from list of layout fields
						Set pageFieldIdentifiers = contentPage.getContentFieldsMap().keySet();
						for ( Iterator i = pageFieldIdentifiers.iterator(); i.hasNext(); ) {
							String fieldIdentifier = (String) i.next();
							contentFields.remove(fieldIdentifier);
						}

						// create content fields for this content page

						ContentFieldManager contentFieldManager = (ContentFieldManager) getBean(Constants.CONTENT_FIELD_MANAGER_BEAN);

						Collection layoutFields = contentFields.values();
						for ( Iterator i = layoutFields.iterator(); i.hasNext(); ) {
							ContentField layoutField = (ContentField) i.next();
                            boolean fieldIsEmpty = layoutField.getContentFieldValues().isEmpty();
                            boolean createField = all
                                    || (empty && fieldIsEmpty)
                                    || (emptyIndexless && fieldIsEmpty && layoutField.getIndex() == null);
							if ( createField ) {
								ContentField pageField = new ContentField();
								WebappUtil.copyProperties(pageField, layoutField, request);
								pageField.setId(null);
								pageField.setContentFieldValues(new ArrayList());
								pageField.setInternal(Boolean.FALSE);
								contentFieldManager.createContentField(pageField, contentPageId);
							}
						}
					}
				}

				// put CP and internal fields in cache
				CacheUtil cacheUtil = CacheUtil.getInstance(request);
                if ( contentPage.getActive().booleanValue() ) {
                    CacheUtil.ContentPageData cpd = new CacheUtil.ContentPageData(contentPage.getId(), layout.getCpDefinition(), WebappUtil.rolesToString(contentPage.getRoles()), contentPage.getCacheMaxAge());
                    cacheUtil.putContentPageInCache(cpd, contentPage.getUri());
                }

                // title
				ContentField titleField = (ContentField) contentPage.getContentFieldsMap().get("title");
				List titleValues = titleField.getContentFieldValues();
				for ( Iterator i = titleValues.iterator(); i.hasNext(); ) {
					ContentFieldValue fieldValue = (ContentFieldValue) i.next();
					// title field is single-lined
					String content = fieldValue.getSimpleValue();
					CacheUtil.CFVData cfvData = new CacheUtil.CFVData(content, titleField.getId(), titleField.getType(), fieldValue.getId(), contentPageId);
					cacheUtil.putPageFieldValueInCache(cfvData, contentPage.getUri(), titleField.getIdentifier(), fieldValue.getContentLocale().getIdentifier());
				}

				// description
				ContentField descriptionField = (ContentField) contentPage.getContentFieldsMap().get("description");
				List descriptionValues = descriptionField.getContentFieldValues();
				for ( Iterator i = descriptionValues.iterator(); i.hasNext(); ) {
					ContentFieldValue fieldValue = (ContentFieldValue) i.next();
					// description field is single-lined
					String content = fieldValue.getSimpleValue();
					CacheUtil.CFVData cfvData = new CacheUtil.CFVData(content, descriptionField.getId(), descriptionField.getType(), fieldValue.getId(), contentPageId);
					cacheUtil.putPageFieldValueInCache(cfvData, contentPage.getUri(), descriptionField.getIdentifier(), fieldValue.getContentLocale().getIdentifier());
				}

				// keywords
				ContentField keywordsField = (ContentField) contentPage.getContentFieldsMap().get("keywords");
				List keywordsValues = keywordsField.getContentFieldValues();
				for ( Iterator i = keywordsValues.iterator(); i.hasNext(); ) {
					ContentFieldValue fieldValue = (ContentFieldValue) i.next();
					// keywords field is single-lined
					String content = fieldValue.getSimpleValue();
					CacheUtil.CFVData cfvData = new CacheUtil.CFVData(content, keywordsField.getId(), keywordsField.getType(), fieldValue.getId(), contentPageId);
					cacheUtil.putPageFieldValueInCache(cfvData, contentPage.getUri(), keywordsField.getIdentifier(), fieldValue.getContentLocale().getIdentifier());
				}

			} catch ( OwnerNotFoundException e ) {
				// layout not found. it might has already been deleted by another transaction
				ActionMessages errors = new ActionMessages();
				errors.add("ownerNotFound", new ActionMessage("core.contentPage.errors.ownerNotFound"));
				saveErrors(request, errors);
                if ( !GenericValidator.isBlankOrNull(requestUrl) ) {
                    session.removeAttribute(WebappConstants.REDIRECT_URL_KEY);
                    return new ActionForward(requestUrl, true);
                }
                return mapping.findForward("listContentPages");
			} catch ( BeanAlreadyExistsException e ) {
				// contentPage already exists
				ActionMessages errors = new ActionMessages();
				errors.add("contentPageAlreadyExists", new ActionMessage("core.contentPage.errors.alreadyExists"));
				saveErrors(request, errors);
				saveToken(request);
				return mapping.getInputForward();
			}
            if ( !GenericValidator.isBlankOrNull(requestUrl) ) {
                session.removeAttribute(WebappConstants.REDIRECT_URL_KEY);
                return new ActionForward(requestUrl, true);
            }
            session.setAttribute(WebappConstants.CONTENT_PAGE_ID_KEY, contentPageId);
            return mapping.findForward("viewContentPage");
		} else {
            if ( !GenericValidator.isBlankOrNull(requestUrl) ) {
                session.removeAttribute(WebappConstants.REDIRECT_URL_KEY);
                return new ActionForward(requestUrl, true);
            }
            return mapping.findForward("listContentPages");
		}
	}
}